#include "stdafx.h"
#include "SelfApp.h"
#include "SceneManage.h"

SelfApp::SelfApp(QWidget *parent)
	: QWidgetD3D(parent)
{
}


SelfApp::~SelfApp()
{
}

void SelfApp::Update(const GameTimer& gt)
{
	QWidgetD3D::Update(gt);
	m_iCurrFrameResourceIndex = (m_iCurrFrameResourceIndex + 1) % gNumFrameResources;
	m_pCurrFrameResource = m_vpFrameObj[m_iCurrFrameResourceIndex];
	if (m_pCurrFrameResource->GetFence() != 0 && mFence->GetCompletedValue() < m_pCurrFrameResource->GetFence())
	{
		HANDLE eventHandle = CreateEventEx(nullptr, false, false, EVENT_ALL_ACCESS);
		ThrowIfFailed(mFence->SetEventOnCompletion(m_pCurrFrameResource->GetFence(), eventHandle));
		WaitForSingleObject(eventHandle, INFINITE);
		CloseHandle(eventHandle);
	}

	ThrowIfFailed(m_pCommandList->Reset(mDirectCmdListAlloc.Get(), nullptr));
	//ThrowIfFailed(m_pCommandList->Reset(m_pCurrFrameResource->CmdListAlloc.Get(), nullptr));
	SceneManage::GetInstance()->UpdateScene(m_pD3DDevice, m_pCommandList);
	m_pCurrFrameResource->UpdateData(ScenRenderItemManage::GetInstance()->GetData(), m_pCommandList);

	ThrowIfFailed(m_pCommandList->Close());
	ID3D12CommandList* cmdsLists[] = { m_pCommandList.Get() };
	mCommandQueue->ExecuteCommandLists(_countof(cmdsLists), cmdsLists);

	FlushCommandQueue();


}

bool SelfApp::Initialize()
{
	if (!QWidgetD3D::Initialize())
		return false;
	
	ThrowIfFailed(m_pCommandList->Reset(mDirectCmdListAlloc.Get(), nullptr));

	SceneManage::GetInstance()->InitScene();
	SceneManage::GetInstance()->CompileScene(m_pD3DDevice, GetBackBufferFormat(), GetDepthStencilFormat(), GetMsaaState(), GetMsaaQuality() );
	
	m_vpFrameObj.resize(3);
	for (int i = 0; i < gNumFrameResources; ++i)
	{
		m_vpFrameObj[i] = shared_ptr<FrameResourceObj>(new FrameResourceObj(m_pD3DDevice));
	}
	TextureObjManage::GetInstance()->Init(m_pD3DDevice, m_pCommandList);


	// Execute the initialization commands.
	ThrowIfFailed(m_pCommandList->Close());
	ID3D12CommandList* cmdsLists[] = { m_pCommandList.Get() };
	mCommandQueue->ExecuteCommandLists(_countof(cmdsLists), cmdsLists);
	FlushCommandQueue();

	// Wait until initializat
	return true;
}

void SelfApp::OnResize()
{
	QWidgetD3D::OnResize();
}


void SelfApp::OnMouseDown(WPARAM btnState, int x, int y)
{
	mLastMousePos.x = x;
	mLastMousePos.y = y;

	SetCapture(this->MainWnd());
}

void SelfApp::Draw(const GameTimer& gt)
{
	QWidgetD3D::Draw(gt);

	auto cmdListAlloc = m_pCurrFrameResource->CmdListAlloc;
	ThrowIfFailed(cmdListAlloc->Reset());
	ThrowIfFailed(m_pCommandList->Reset(cmdListAlloc.Get(), nullptr));
	m_pCommandList->RSSetViewports(1, &mScreenViewport);
	m_pCommandList->RSSetScissorRects(1, &mScissorRect);

	// Indicate a state transition on the resource usage.
	m_pCommandList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(CurrentBackBuffer(),
		D3D12_RESOURCE_STATE_PRESENT, D3D12_RESOURCE_STATE_RENDER_TARGET));

	// Clear the back buffer and depth buffer.
	m_pCommandList->ClearRenderTargetView(CurrentBackBufferView(), Colors::LightSteelBlue, 0, nullptr);
	m_pCommandList->ClearDepthStencilView(DepthStencilView(), D3D12_CLEAR_FLAG_DEPTH | D3D12_CLEAR_FLAG_STENCIL, 1.0f, 0, 0, nullptr);

	// Specify the buffers we are going to render to.
	m_pCommandList->OMSetRenderTargets(1, &CurrentBackBufferView(), true, &DepthStencilView());

	map<string, vector<int>> mapEffectGroup;
	vector<ScenRenderItem> vRenderItem = m_pCurrFrameResource->m_vRenderItem;
	for (int i=0;i<vRenderItem.size(); ++i)
	{
		string sEffect = EffectManage::GetInstance()->FindEffectName(vRenderItem[i].m_strPipeShaderName);
		mapEffectGroup[sEffect].push_back(i);
	}
	for (map<string, vector<int>>::iterator pos = mapEffectGroup.begin(); pos != mapEffectGroup.end(); ++pos)
	{
		shared_ptr<EffectObj> pEffect = EffectManage::GetInstance()->Get(pos->first);
		pEffect->Bind(m_pCommandList, m_pCurrFrameResource);
		for (int inx : pos->second)
		{
			string pipeName = vRenderItem[inx].m_strPipeShaderName;
			pEffect->BindPipeState(m_pCommandList, pipeName);

			
			m_pCommandList->IASetVertexBuffers(0, 1, &(vRenderItem[inx].m_pStandGeo->VertexBufferView()));
			m_pCommandList->IASetIndexBuffer(&vRenderItem[inx].m_pStandGeo->IndexBufferView());
			m_pCommandList->IASetPrimitiveTopology(vRenderItem[inx].m_PrimitiveType);
			m_pCurrFrameResource->BindObjResource(vRenderItem[inx].m_inxCBObj);
			m_pCommandList->DrawIndexedInstanced(vRenderItem[inx].m_IndexCount, 1, vRenderItem[inx].m_StartIndexLocation, vRenderItem[inx].m_BaseVertexLocation, 0);

		}

	}

	// Done recording commands.
	ThrowIfFailed(m_pCommandList->Close());

	// Add the command list to the queue for execution.
	ID3D12CommandList* cmdsLists[] = { m_pCommandList.Get() };
	mCommandQueue->ExecuteCommandLists(_countof(cmdsLists), cmdsLists);

	// swap the back and front buffers
	ThrowIfFailed(mSwapChain->Present(0, 0));
	mCurrBackBuffer = (mCurrBackBuffer + 1) % SwapChainBufferCount;

	// Wait until frame commands are complete.  This waiting is inefficient and is
	// done for simplicity.  Later we will show how to organize our rendering code
	// so we do not have to wait per frame.
	FlushCommandQueue();
}

void SelfApp::OnMouseUp(WPARAM btnState, int x, int y)
{
	ReleaseCapture();
}

void SelfApp::OnMouseMove(WPARAM btnState, int x, int y)
{
	if ((btnState & MK_LBUTTON) != 0)
	{
		// Make each pixel correspond to a quarter of a degree.
		float dx = XMConvertToRadians(0.25f*static_cast<float>(x - mLastMousePos.x));
		float dy = XMConvertToRadians(0.25f*static_cast<float>(y - mLastMousePos.y));
		shared_ptr<CameraConstants> pCam = CameraManage::GetInstance()->Get("MainCam");

		pCam->Pitch(dy);
		pCam->RotateY(dx);
	}

	mLastMousePos.x = x;
	mLastMousePos.y = y;
}

void SelfApp::StateKeyChange(int iKey, bool isDown)
{
	const float dt = 1;
	shared_ptr<CameraConstants> pCam = CameraManage::GetInstance()->Get("MainCam");

	if (iKey == 'W')
		pCam->Walk(10.0f*dt);

	if (iKey == 'S')
		pCam->Walk(-10.0f*dt);

	if (iKey == 'A' )
		pCam->Strafe(-10.0f*dt);

	if (iKey == 'D')
		pCam->Strafe(10.0f*dt);

	pCam->UpdateViewMatrix();
}
